using System;
using System.Collections;
using System.Reflection;

namespace bOS.CLR
{
	public class AssemblyError : System.Exception
	{
		public AssemblyError(string sError) : base(sError)
		{
		}
	}


	public class DynaClassInfo
	{
		public Type type;
		public Object ClassObject;

		public DynaClassInfo()
		{
		}

		public DynaClassInfo(Type t, Object c)
		{
			type = t;
			ClassObject = c;
		}
	}

	public class AssemblyManager
	{
		public static Hashtable AssemblyReferences = new Hashtable();
		public static Hashtable ClassReferences = new Hashtable();

		public AssemblyManager()
		{
			//
			// TODO: Add constructor logic here
			//
		}
		

		public static int RunScript(Assembly assembly, string entryPoint)
		{
			try
			{
				//cant call the entry method if the assembly is null
				if (assembly != null)
				{
					//Use reflection to call the static Main function
					Module[] mods = assembly.GetModules(false);
					Type[] types = mods[0].GetTypes();

					//loop through each class that was defined and look for the first occurrance of the entry point method
					foreach (Type type in types)
					{
						MethodInfo mi = type.GetMethod(entryPoint, BindingFlags.Public | BindingFlags.Static);
						if (mi != null)
						{
							if (mi.ReturnType.Name != "Int32")
							{
								//if the entry point method doesnt return an Int32, then return the error constant
								mi.Invoke(null, null);
								return -1;
							}
							else
							{
								//if the entry point method does return in Int32, then capture it and return it
								return (int)mi.Invoke(null, null);
							}
						}
					}

					throw new AssemblyError ("Dot Net Script Engine could not find the public static " + entryPoint + " entry function to execute");
				}
			}
			catch (System.Exception)
			{
				throw;
			}

			return -1;
		}


		public static Object InvokeMethodSlow(string AssemblyName, 
											  string ClassName,
											  string MethodName,
											  Object[] args)
		{
			// load the assemly
			Assembly assembly = Assembly.LoadFrom(AssemblyName);

			// Walk through each type in the assembly looking for our class
			foreach (Type type in assembly.GetTypes())
			{
				if ( (type.IsClass == true) && (type.FullName.CompareTo (ClassName) == 0 ) )
				{
					// create an instance of the object
					object ClassObj = Activator.CreateInstance(type);

					// Dynamically Invoke the method
					object Result = type.InvokeMember(MethodName,
						BindingFlags.Default | BindingFlags.InvokeMethod,
						null,
						ClassObj,
						args);
					return (Result);
				}
			}
			throw (new System.Exception("could not invoke method"));
		}

		
		public static DynaClassInfo GetClassReference(string AssemblyName, 
													  string ClassName)
		{
			if (ClassReferences.ContainsKey(AssemblyName) == false)
			{
				Assembly assembly;
				if (AssemblyReferences.ContainsKey(AssemblyName) == false)
				{
					AssemblyReferences.Add(AssemblyName, 
						assembly = Assembly.LoadFrom(AssemblyName));
				}
				else
					assembly = (Assembly)AssemblyReferences[AssemblyName];

				// Walk through each type in the assembly
				foreach (Type type in assembly.GetTypes())
				{
					if ( (type.IsClass == true) && (type.FullName.CompareTo (ClassName) == 0 ) )
					{
						DynaClassInfo ci = new DynaClassInfo(type, 
							Activator.CreateInstance(type));
						ClassReferences.Add(AssemblyName, ci);
						return (ci);
					}
				}
				throw (new System.Exception("could not instantiate class"));
			}
			return ((DynaClassInfo)ClassReferences[AssemblyName]);
		}


		public static Object InvokeMethod(DynaClassInfo ci,	
										  string MethodName,
										  Object[] args)
		{
			// Dynamically Invoke the method
			Object Result = ci.type.InvokeMember(MethodName,
				BindingFlags.Default | BindingFlags.InvokeMethod,
				null,
				ci.ClassObject,
				args);
			return (Result);
		}

		
		/*
		 *	Object [] args = {1, "2", 3.0};
			Object Result = DynaInvoke("c:\FullPathToDll.DLL", 
			"ClassName", "MethodName", args);
		*/
		public static Object InvokeMethod(string AssemblyName, 
										  string ClassName,
										  string MethodName,
										  Object[] args)
		{
			DynaClassInfo ci = GetClassReference(AssemblyName, ClassName);
			return (InvokeMethod(ci, MethodName, args));
		}
	}
}
